package com.google.android.volley;

import java.io.ByteArrayOutputStream;
import java.io.IOException;

import org.apache.http.ConnectionReuseStrategy;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.HttpResponse;
import org.apache.http.client.AuthenticationHandler;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.RedirectHandler;
import org.apache.http.client.RequestDirector;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.UserTokenHandler;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.params.HttpClientParams;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.ConnectionKeepAliveStrategy;
import org.apache.http.conn.params.ConnManagerParamBean;
import org.apache.http.conn.params.ConnPerRouteBean;
import org.apache.http.conn.routing.HttpRoutePlanner;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.client.RequestWrapper;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.BasicHttpProcessor;
import org.apache.http.protocol.HttpContext;
import org.apache.http.protocol.HttpProcessor;
import org.apache.http.protocol.HttpRequestExecutor;

import android.content.Context;
import android.net.SSLCertificateSocketFactory;
import android.os.Looper;
import android.util.Log;

import com.google.android.volley.elegant.ElegantPlainSocketFactory;
import com.google.android.volley.elegant.ElegantRequestDirector;
import com.google.android.volley.elegant.ElegantSSLSocketFactory;
import com.google.android.volley.elegant.ElegantThreadSafeConnManager;

public final class AndroidHttpClient implements HttpClient {

    private class CurlLogger implements HttpRequestInterceptor {

        public void process(HttpRequest httprequest, HttpContext httpcontext) throws IOException {
            LoggingConfiguration loggingconfiguration = curlConfiguration;
            if(loggingconfiguration != null && loggingconfiguration.isLoggable() && (httprequest instanceof HttpUriRequest))
                loggingconfiguration.println( AndroidHttpClient.toCurl( (HttpUriRequest)httprequest, true));
        }

    }

    private static class LoggingConfiguration {

        private boolean isLoggable() {
            return Log.isLoggable( tag, level);
        }

        private void println(String s) {
            Log.println( level, tag, s);
        }

        private final int level;

        private final String tag;

        private LoggingConfiguration(String s, int i) {
            tag = s;
            level = i;
        }

    }

    private AndroidHttpClient(ClientConnectionManager clientconnectionmanager, HttpParams httpparams) {
        mLeakedException = new IllegalStateException( "AndroidHttpClient created and never closed");
        _flddelegate = new DefaultHttpClient( clientconnectionmanager, httpparams) {

            protected RequestDirector createClientRequestDirector(HttpRequestExecutor httprequestexecutor,
                ClientConnectionManager clientconnectionmanager1, ConnectionReuseStrategy connectionreusestrategy,
                ConnectionKeepAliveStrategy connectionkeepalivestrategy, HttpRoutePlanner httprouteplanner,
                HttpProcessor httpprocessor, HttpRequestRetryHandler httprequestretryhandler, RedirectHandler redirecthandler,
                AuthenticationHandler authenticationhandler, AuthenticationHandler authenticationhandler1,
                UserTokenHandler usertokenhandler, HttpParams httpparams1) {
                return new ElegantRequestDirector( httprequestexecutor, clientconnectionmanager1, connectionreusestrategy,
                    connectionkeepalivestrategy, httprouteplanner, httpprocessor, httprequestretryhandler, redirecthandler,
                    authenticationhandler, authenticationhandler1, usertokenhandler, httpparams1);
            }

            protected HttpContext createHttpContext() {
                BasicHttpContext basichttpcontext = new BasicHttpContext();
                basichttpcontext.setAttribute( "http.authscheme-registry", getAuthSchemes());
                basichttpcontext.setAttribute( "http.cookiespec-registry", getCookieSpecs());
                basichttpcontext.setAttribute( "http.auth.credentials-provider", getCredentialsProvider());
                return basichttpcontext;
            }

            protected BasicHttpProcessor createHttpProcessor() {
                BasicHttpProcessor basichttpprocessor = super.createHttpProcessor();
                basichttpprocessor.addRequestInterceptor( AndroidHttpClient.sThreadCheckInterceptor);
                basichttpprocessor.addRequestInterceptor( new CurlLogger());
                return basichttpprocessor;
            }
        };
    }

    public static AndroidHttpClient newInstance(String s, Context context) {
        BasicHttpParams basichttpparams = new BasicHttpParams();
        HttpConnectionParams.setStaleCheckingEnabled( basichttpparams, false);
        HttpConnectionParams.setConnectionTimeout( basichttpparams, 20000);
        HttpConnectionParams.setSoTimeout( basichttpparams, 20000);
        HttpConnectionParams.setSocketBufferSize( basichttpparams, 8192);
        HttpClientParams.setRedirecting( basichttpparams, true);
        // SSLSessionCache sslsessioncache;
        SchemeRegistry schemeregistry;
        ConnManagerParamBean connmanagerparambean;
        // if(context == null)
        // sslsessioncache=null;
        // else
        // sslsessioncache=new SSLSessionCache(context);
        HttpProtocolParams.setUserAgent( basichttpparams, s);
        schemeregistry = new SchemeRegistry();
        schemeregistry.register( new Scheme( "http", ElegantPlainSocketFactory.getSocketFactory(), 80));
        schemeregistry.register( new Scheme( "file", ElegantPlainSocketFactory.getSocketFactory(), 80));
        schemeregistry.register( new Scheme( "package", ElegantPlainSocketFactory.getSocketFactory(), 80));
        schemeregistry.register( new Scheme( "https", ElegantSSLSocketFactory.getSocketFactory(), 443));
        connmanagerparambean = new ConnManagerParamBean( basichttpparams);
        connmanagerparambean.setConnectionsPerRoute( new ConnPerRouteBean( 4));
        connmanagerparambean.setMaxTotalConnections( 8);
        return new AndroidHttpClient( new ElegantThreadSafeConnManager( basichttpparams, schemeregistry), basichttpparams);
    }

    private static String toCurl(HttpUriRequest httpurirequest, boolean flag) throws IOException {
        StringBuilder stringbuilder = new StringBuilder();
        stringbuilder.append( "curl ");
        Header aheader[] = httpurirequest.getAllHeaders();
        int i = aheader.length;
        int j = 0;
        while(j < i) {
            Header header = aheader[j];
            if(flag || !header.getName().equals( "Authorization") && !header.getName().equals( "Cookie")) {
                stringbuilder.append( "--header \"");
                stringbuilder.append( header.toString().trim());
                stringbuilder.append( "\" ");
            }
            j++;
        }
        java.net.URI uri = httpurirequest.getURI();
        if(httpurirequest instanceof RequestWrapper) {
            HttpRequest httprequest = ((RequestWrapper)httpurirequest).getOriginal();
            if(httprequest instanceof HttpUriRequest)
                uri = ((HttpUriRequest)httprequest).getURI();
        }
        stringbuilder.append( "\"");
        stringbuilder.append( uri);
        stringbuilder.append( "\"");
        if(httpurirequest instanceof HttpEntityEnclosingRequest) {
            HttpEntity httpentity = ((HttpEntityEnclosingRequest)httpurirequest).getEntity();
            if(httpentity != null && httpentity.isRepeatable())
                if(httpentity.getContentLength() < 1024L) {
                    ByteArrayOutputStream bytearrayoutputstream = new ByteArrayOutputStream();
                    httpentity.writeTo( bytearrayoutputstream);
                    String s = bytearrayoutputstream.toString();
                    stringbuilder.append( " --data-ascii \"").append( s).append( "\"");
                }
                else {
                    stringbuilder.append( " [TOO MUCH DATA TO INCLUDE]");
                }
        }
        return stringbuilder.toString();
    }

    public void enableCurlLogging(String s, int i) {
        if(s == null)
            throw new NullPointerException( "name");
        if(i < 2 || i > 7) {
            throw new IllegalArgumentException( "Level is out of range [2..7]");
        }
        else {
            curlConfiguration = new LoggingConfiguration( s, i);
            return;
        }
    }

    public <T> T execute(HttpHost httphost, HttpRequest httprequest, ResponseHandler<? extends T> responsehandler)
        throws IOException, ClientProtocolException {
        return _flddelegate.execute( httphost, httprequest, responsehandler);
    }

    public <T> T execute(HttpHost httphost, HttpRequest httprequest, ResponseHandler<? extends T> responsehandler,
        HttpContext httpcontext) throws IOException, ClientProtocolException {
        return _flddelegate.execute( httphost, httprequest, responsehandler, httpcontext);
    }

    public <T> T execute(HttpUriRequest httpurirequest, ResponseHandler<? extends T> responsehandler) throws IOException,
        ClientProtocolException {
        return _flddelegate.execute( httpurirequest, responsehandler);
    }

    public <T> T execute(HttpUriRequest httpurirequest, ResponseHandler<? extends T> responsehandler, HttpContext httpcontext)
        throws IOException, ClientProtocolException {
        return _flddelegate.execute( httpurirequest, responsehandler, httpcontext);
    }

    public HttpResponse execute(HttpHost httphost, HttpRequest httprequest) throws IOException {
        return _flddelegate.execute( httphost, httprequest);
    }

    public HttpResponse execute(HttpHost httphost, HttpRequest httprequest, HttpContext httpcontext) throws IOException {
        return _flddelegate.execute( httphost, httprequest, httpcontext);
    }

    public HttpResponse execute(HttpUriRequest httpurirequest) throws IOException {
        return _flddelegate.execute( httpurirequest);
    }

    public HttpResponse execute(HttpUriRequest httpurirequest, HttpContext httpcontext) throws IOException {
        return _flddelegate.execute( httpurirequest, httpcontext);
    }

    protected void finalize() throws Throwable {
        super.finalize();
        if(mLeakedException != null) {
            Log.e( "AndroidHttpClient", "Leak found", mLeakedException);
            mLeakedException = null;
        }
    }

    public ClientConnectionManager getConnectionManager() {
        return _flddelegate.getConnectionManager();
    }

    public HttpParams getParams() {
        return _flddelegate.getParams();
    }

    public static long DEFAULT_SYNC_MIN_GZIP_BYTES = 256L;

    private static final HttpRequestInterceptor sThreadCheckInterceptor = new HttpRequestInterceptor() {

        public void process(HttpRequest httprequest, HttpContext httpcontext) {
            if(Looper.myLooper() != null && Looper.myLooper() == Looper.getMainLooper())
                throw new RuntimeException( "This thread forbids HTTP requests");
            else
                return;
        }

    };

    private volatile LoggingConfiguration curlConfiguration;

    private final HttpClient _flddelegate;

    private RuntimeException mLeakedException;

}
